import prompts from "prompts";
import {
  Prompts_Build_Mode_Enum,
  Prompts_Build_Type_Enum,
  Status_Color_Enum,
} from "../enum/app.enum";
import path, { resolve } from "path";
import { writeFileSync, mkdirSync, accessSync } from "fs";
import chalk from "chalk";
import { Project, Node, type ModuleDeclaration } from "ts-morph";
import { Listr, type ListrTask } from "listr2";
import { elementCommonList } from "../config/source";

interface PromptsBuildOptions {
  type: Prompts_Build_Type_Enum;
  modes: Array<Prompts_Build_Mode_Enum>;
  name: string;
  contain: string;
}

function styleLogs(data: any, color: string = Status_Color_Enum.SUCCESS): void {
  if (data) {
    let log = "";
    //转为字符串然后分割换行符
    data = data.toString().split(/\r?\n/);

    if (data?.length > 1) {
      data.forEach((val: string) => {
        val && (log += val + "\n");
      });
    } else log = data?.[0];

    console.log(chalk[color].bold(log));
  }
}

async function buildPage(opts: PromptsBuildOptions) {
  return new Promise((res, rej) => {
    const { name } = opts;

    const pageTemp = `import { defineComponent } from "vue";

export default defineComponent({
  setup() {
    return () => <div class="${name.toLocaleLowerCase()}-container">is ${name} working!</div>;
  },
});
 `;

    const cssTemp = `.${name.toLocaleLowerCase()}-container {
  background-color: #fff;
  width: 100%;
  height: 100%;
}
`;

    const baseViewPath = resolve("src", "application", "view", name);

    try {
      accessSync(baseViewPath);
      styleLogs(
        "Build Error：The Page Already Exists",
        Status_Color_Enum.ERROR
      );
      rej(0);
    } catch {
      mkdirSync(baseViewPath, { recursive: true });
      writeFileSync(path.join(baseViewPath, `index.tsx`), pageTemp, {
        encoding: "utf8",
      });
      writeFileSync(path.join(baseViewPath, `index.module.css`), cssTemp, {
        encoding: "utf8",
      });
      res(1);
    }
  });
}

async function buildCommon(opts: PromptsBuildOptions) {
  return new Promise((res, rej) => {
    const { name, contain } = opts;

    const pageTemp = `${
      contain ? `import type { ${contain}Props } from "element-plus";` : ``
    }
import { defineComponent } from "vue";

export interface ${name}Params${
      contain ? ` extends Partial<${contain}Props>` : ``
    } {

}

export default defineComponent<${name}Params>({
  name: "${name}",
  setup(props, { slots }) {
    return () => (
      ${
        contain
          ? `<El${contain} class="${name.toLocaleLowerCase()}-container" {...props}>{slots}</El${contain}>`
          : `<div class="${name.toLocaleLowerCase()}-container" {...props}>{slots}</div>`
      }
    );
  },
});

 `;

    const cssTemp = `.${name.toLocaleLowerCase()}-container {

}
`;

    const baseViewPath = resolve("src", "application", "common", name);

    try {
      accessSync(baseViewPath);
      styleLogs(
        "Build Error：The Common Already Exists",
        Status_Color_Enum.ERROR
      );
      rej(0);
    } catch {
      mkdirSync(baseViewPath, { recursive: true });
      writeFileSync(path.join(baseViewPath, `index.tsx`), pageTemp, {
        encoding: "utf8",
      });
      writeFileSync(path.join(baseViewPath, `index.module.css`), cssTemp, {
        encoding: "utf8",
      });
      res(1);
    }
  });
}

function changeRouterAst(opts: PromptsBuildOptions) {
  return new Promise((res, rej) => {
    const { type, modes, name } = opts;
    const project = new Project();
    const sourceFile = project.addSourceFileAtPath(
      path.resolve("src", "application", "router", "index.ts")
    );

    const node = sourceFile.forEachDescendant((node) => {
      if (Node.isPropertyAssignment(node)) {
        const firstChild = node.getFirstChild();
        if (
          Node.isIdentifier(firstChild) &&
          firstChild.getText() === "routes"
        ) {
          return node.getChildren();
        }
        return;
      }
      return undefined;
    });

    if (node) {
      node.forEach((item) => {
        if (Node.isArrayLiteralExpression(item)) {
          item.addElement(
            `{\n path: "/${name.toLocaleLowerCase()}",\n name: "${name}",\n component: () => import("@application/view/${name}/index"),\n}`
          );
        }
      });
    }

    sourceFile
      ?.save()
      .then(() => {
        res(1);
      })
      .catch((err) => {
        styleLogs(
          `Build Error：${err?.message ?? err}`,
          Status_Color_Enum.ERROR
        );
        rej(0);
      });
  });
}

function changeContainTypeAst(opts: PromptsBuildOptions) {
  return new Promise((res, rej) => {
    const { contain } = opts;
    const project = new Project();
    const sourceFile = project.addSourceFileAtPath(
      path.resolve("src", "application", "component.d.ts")
    );

    const node = sourceFile.forEachDescendant((node) => {
      if (Node.isIdentifier(node) && node.getText() === "global") {
        const parentNode = node.getParent();
        if (Node.isModuleDeclaration(parentNode)) {
          return parentNode;
        }
        return;
      }
      return undefined;
    });

    if (node) {
      const nodeStr = node.getBodyText();
      const appendStr = `const El${contain}: typeof import("element-plus/es")["El${contain}"];`;

      if (!nodeStr?.includes(appendStr))
        node.setBodyText(`${nodeStr}\n${appendStr}`);
    }

    sourceFile
      ?.save()
      .then(() => {
        res(1);
      })
      .catch((err) => {
        styleLogs(
          `Build Error：${err?.message ?? err}`,
          Status_Color_Enum.ERROR
        );
        rej(0);
      });
  });
}

function changeCommonTypeAst(opts: PromptsBuildOptions) {
  return new Promise((res, rej) => {
    const { name } = opts;
    const project = new Project();
    const sourceFile = project.addSourceFileAtPath(
      path.resolve("src", "application", "common", "common.d.ts")
    );
    const appendStr = `declare const ${name}:DefineComponent<BaseParams<import("./${name}").${name}Params>>`;

    if (!sourceFile.getText()?.includes(appendStr))
      sourceFile.addStatements(`\n${appendStr}`);

    sourceFile
      ?.save()
      .then(() => {
        res(1);
      })
      .catch((err) => {
        styleLogs(
          `Build Error：${err?.message ?? err}`,
          Status_Color_Enum.ERROR
        );
        rej(0);
      });
  });
}
async function constructStart() {
  let cancel = false;
  //取消选择
  const onCancel = (prompt) => {
    styleLogs("Build Error：Missing Options", Status_Color_Enum.ERROR);
    cancel = true;
    return false;
  };

  const questions = [
    {
      type: "select",
      name: "type",
      message: "Select a type of the build target:",
      choices: [
        {
          title: "Page",
          description: "页面",
          value: Prompts_Build_Type_Enum.PAGE,
        },
        {
          title: "Component",
          description: "基础组件",
          value: Prompts_Build_Type_Enum.COMMON,
        },
      ],
    },
    {
      type: (prev) => (prev == Prompts_Build_Type_Enum.PAGE ? "select" : null),
      name: "modes",
      instructions: false,
      message: "Select Page mode:",
      choices: [
        {
          title: "General",
          description: "通用配置",
          value: Prompts_Build_Mode_Enum.GENERAL,
        },
      ],
    },
    {
      type: (prev) =>
        prev == Prompts_Build_Type_Enum.COMMON ? "select" : null,
      name: "modes",
      instructions: false,
      message: "Select Common mode:",
      choices: [
        {
          title: "General",
          description: "通用配置",
          value: Prompts_Build_Mode_Enum.GENERAL,
        },
        {
          title: "ContainCommon",
          description: "包含组件",
          value: Prompts_Build_Mode_Enum.CONTAIN_COMMON,
        },
      ],
    },
    {
      type: (prev) =>
        prev == Prompts_Build_Mode_Enum.CONTAIN_COMMON ? "select" : null,
      name: "contain",
      instructions: false,
      message: "Select The Included Foundation Common:",
      choices: elementCommonList.map((item: Record<string, any>) => ({
        title: item.value,
        value: item.value,
        description: item.label,
      })),
    },
    {
      type: "text",
      name: "name",
      message: "Input is name:",
      validate: (value) => (!value ? `Pls input bundle name` : true),
      when: (res) => res.length > 0,
    },
  ];

  const promptRes: PromptsBuildOptions = await prompts(questions, { onCancel });

  // 根据用户的选择输出信息
  if (!cancel) {
    styleLogs("Configuration Completed, Start Building");

    let fn = (cb: Function) => cb?.bind(cb, promptRes);

    const astTaskList: Array<
      ListrTask & { validate: (promptRes: PromptsBuildOptions) => boolean }
    > = [
      {
        validate: (promptRes) =>
          promptRes.type === Prompts_Build_Type_Enum.PAGE,
        title: "Router Page AST Start Chang...",
        task: fn(changeRouterAst),
      },
      {
        validate: (promptRes) =>
          promptRes.type === Prompts_Build_Type_Enum.COMMON,
        title: "Common Type Ast Start Chang...",
        task: fn(changeCommonTypeAst),
      },
      {
        validate: (promptRes) =>
          promptRes.type === Prompts_Build_Type_Enum.COMMON &&
          !!promptRes.contain,
        title: "Contain Type Ast Start Chang...",
        task: fn(changeContainTypeAst),
      },
    ];

    const tasksLister = new Listr(
      [
        {
          title: "Render Template Start Building...",
          task: fn(
            promptRes.type === Prompts_Build_Type_Enum.PAGE
              ? buildPage
              : buildCommon
          ),
        },
        ...astTaskList.filter((item) => item.validate(promptRes)),
      ],
      {
        exitOnError: true,
      }
    );
    await tasksLister.run();

    styleLogs(`${promptRes.name} Build finish!`);
  }
}

constructStart();
